package xyz.riceball.context;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.riceball.annotation.Autowired;
import xyz.riceball.annotation.Component;
import xyz.riceball.annotation.Qualifier;
import xyz.riceball.annotation.Service;
import xyz.riceball.beans.BeanDefinition;
import xyz.riceball.beans.GenericBeanDefinition;
import xyz.riceball.exception.NoSuchBeanDefinitionException;
import xyz.riceball.exception.NoUniqueBeanDefinitionException;
import xyz.riceball.factory.PreBuildBeanFactory;
import xyz.riceball.util.ClassUtil;
import xyz.riceball.util.MapUtil;
import xyz.riceball.util.StringUtil;

import java.lang.reflect.Field;
import java.util.*;

/**
 * <p>
 *
 * </p>
 *
 * @author xiaovcloud
 * @since 2021/7/2 21:38
 */
public abstract class AbstractApplicationContext implements ApplicationContext {

    private final Logger log = LoggerFactory.getLogger(getClass());
    /**
     * 生产bean的工厂
     */
    private final PreBuildBeanFactory factory = new PreBuildBeanFactory();

    public AbstractApplicationContext() {
    }

    /**
     * 启动容器
     *
     * @param basePackages 传入的包路径集合
     */
    public AbstractApplicationContext(String[] basePackages) {
        //扫描包
        doScanner(basePackages);
        //实例化bean
        doInstance();
        //依赖注入
        doAutowired();
    }

    /**
     * 扫描包
     *
     * @param packs 扫描的包路径数组
     */
    private void doScanner(String[] packs) {
        for (String pack : packs) {
            //获取所有的文件类
            Set<Class<?>> classes = ClassUtil.getClasses(pack);
            for (Class<?> clazz : classes) {
                //遍历，找到加了注解的类
                Component componentAnnotation = clazz.getAnnotation(Component.class);
                Service serviceAnnotation = clazz.getAnnotation(Service.class);
                if (componentAnnotation != null || serviceAnnotation != null) {
                    //获取注解的值，以service注解为主
                    String beanName = serviceAnnotation != null ?
                            serviceAnnotation.value() : componentAnnotation.value();
                    if ("".equals(beanName)) {
                        String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
                        beanName = StringUtil.toLowerCaseFirstOne(className);
                    }
                    //生成bean定义信息,注册bean
                    GenericBeanDefinition definition = new GenericBeanDefinition();
                    definition.setBeanClass(clazz);
                    factory.registerBeanDefinition(beanName, definition);
                }
            }
        }
    }

    /**
     * 创建实例对象
     */
    private void doInstance() {
        try {
            factory.preInstantiateSingletons();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 依赖注入
     */
    private void doAutowired() {
        List<String> beanNames = factory.getBeanNames();
        Map<String, BeanDefinition> beanDefinitionMap = factory.getBeanDefinitionMap();
        for (String beanName : beanNames) {
            //外层bean的class
            Class<?> clazz = beanDefinitionMap.get(beanName).getBeanClass();
            //所有的字段
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
                //存在Autowired注解的字段
                if (autowiredAnnotation != null) {
                    try {
                        //直接根据类型，如果没有或者多个再判断名字
                        //当前字段的class类型
                        Class<?> requiredType = declaredField.getType();
                        Object bean = getBean(requiredType);
                        //没有具体类型的bean匹配
                        //取相同类型，如果有多个，根据Qualifier再取名字
                        if (bean == null) {
                            //临时存放接口下的bean
                            Map<String, Object> interfaceMap = new HashMap<>(256);
                            Map<String, Object> beanMap = factory.getBeanMap();
                            // 如果为接口，查询所有子类
                            if (requiredType.isInterface()) {
                                Set<Class<?>> byInterface = ClassUtil.getAllClassByInterface(requiredType);
                                for (Class<?> cla : byInterface) {
                                    Object o = factory.getBean(cla);
                                    if (o != null) {
                                        String bName = MapUtil.getFirstKey(beanMap, o);
                                        interfaceMap.put(bName, o);
                                    }
                                }
                            }
                            if (interfaceMap.size() <= 0) {
                                throw new NoSuchBeanDefinitionException(requiredType);
                            } else if (interfaceMap.size() == 1) {
                                //一个，直接返回
                                String key = interfaceMap.keySet().toArray(new String[]{})[0];
                                bean = interfaceMap.get(key);
                            } else {
                                //多个根据name返回
                                Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
                                if (qualifier != null) {
                                    //有名字
                                    String bName = qualifier.value();
                                    bean = factory.getBean(bName);
                                    if (bean == null) {
                                        throw new NoSuchBeanDefinitionException(bName, requiredType);
                                    }
                                } else {
                                    //没有名字，但是有多个bean，直接抛异常
                                    throw new NoUniqueBeanDefinitionException(requiredType, interfaceMap.keySet());
                                }
                            }
                        }
                        //获取外层的对象
                        Object object = factory.getBean(clazz);
                        declaredField.setAccessible(true);
                        declaredField.set(object, bean);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    @Override
    public Object getBean(String beanName) {
        try {
            return factory.getBean(beanName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new NoSuchBeanDefinitionException(beanName);
    }

    @Override
    public <T> T getBean(String beanName, Class<T> requiredType) {
        try {
            return factory.getBean(beanName, requiredType);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new NoSuchBeanDefinitionException(beanName, requiredType);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) {
        try {
            return factory.getBean(requiredType);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new NoSuchBeanDefinitionException(requiredType);
    }

    @Override
    public boolean containsBean(String name) {
        try {
            return factory.containsBean(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public String getApplicationName() {
        return "";
    }


}
